/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package gride.plugin.projectbrowser;

import gride.events.GrideEvents;
import gride.events.EventListener;
import gride.exceptions.UserCancelException;
import gride.gui.GUIManager;
import gride.handlers.RenameMoveScriptHandler;
import gride.plugin.GridePlugin;
import gride.project.GrideScript;
import gride.project.Project;
import gride.groovy.Utils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 *
 * @author lukasz
 */
public class ProjectBrowserPlugin extends GridePlugin
{
    private JList projectsList;

    private JTree projectTree;
    private ProjectTreeModel treeModel;
    private TooltipCellRenderer cellRenderer;

    JMenuItem excludeContextMenuItem;
    JMenuItem setAsMainContextMenuItem;
    JMenuItem renameContextMenuItem;


    /**
     * 
     */
    public ProjectBrowserPlugin()
    {
        super();

        // create the projects list component
        projectsList = new JList();
        projectsList.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                if (e.getButton() == MouseEvent.BUTTON1 &&
                    e.getClickCount() == 2)
                {
                    if (projectsList.getSelectedIndex() == 0)
                    {
                        // create a new project clicked
                        try
                        {
                            projectManager.newProjectAction();
                        }
                        catch (UserCancelException ex)
                        {
                            System.out.println("User cancelled creating a " +
                                               "new project");
                            return;
                        }
                    }
                    else
                    {
                        // one of the existing projects clicked
                        try
                        {
                        projectManager.openProjectNamed(
                                       (String)projectsList.getSelectedValue());
                        }
                        catch (UserCancelException ex)
                        {
                            System.out.println("User cancelled opening the " +
                                               "project");
                            return;
                        }
                    }
                }
            }
        });


        // create the project browser component
        projectTree = new JTree();

        // set a cell renderer that will provide tooltips for tree items
        cellRenderer = new TooltipCellRenderer();
        projectTree.setCellRenderer(cellRenderer);

        // register with the tooltip system
        ToolTipManager.sharedInstance().registerComponent(projectTree);
        ToolTipManager.sharedInstance().setInitialDelay(0);

        // only allow a single element to be selected
        projectTree.getSelectionModel().setSelectionMode(
                                    TreeSelectionModel.SINGLE_TREE_SELECTION);
        
        // add a listener raising OPEN_FILE event
        // when a file is double-clicked on the tree
        projectTree.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                TreePath treePath =
                        projectTree.getPathForLocation(e.getX(), e.getY());

                if (treePath != null)
                {
                    projectTree.setSelectionPath(treePath);
                    
                    ProjectTreeNode node =
                            (ProjectTreeNode) treePath.getLastPathComponent();

                    if (e.getButton() == MouseEvent.BUTTON3)
                    {
                        if (node.getType() == ProjectTreeNode.SCRIPT ||
                            node.getType() == ProjectTreeNode.LIBRARY)
                        {
                            JPopupMenu menu = new JPopupMenu();

                            menu.add(excludeContextMenuItem);

                            if (node.getType() == ProjectTreeNode.SCRIPT)
                            {
                                GrideScript script = 
                                        (GrideScript) node.getObject();
                                
                                if (!script.isMainScript())
                                    menu.add(setAsMainContextMenuItem);

                                menu.add(renameContextMenuItem);
                            }

                            menu.show(projectTree, e.getX(), e.getY());
                        }
                    }

                    // when double-clicked a leaf node
                    if (e.getButton() == MouseEvent.BUTTON1 &&
                        e.getClickCount() == 2)
                    {
                        if (node.getType() == ProjectTreeNode.SCRIPT)
                        {
                            Object[] args = { node.getObject() };
                            eventManager.raiseEvent(GrideEvents.OPEN_FILE,
                                                    args);
                        }
                    }
                }
            }
        });

        ActionListener excludeListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                ProjectTreeNode node = (ProjectTreeNode)
                        projectTree.getSelectionPath().getLastPathComponent();

                int answer = JOptionPane.showConfirmDialog(null,
                                "Are you sure you want to exclude " +
                                node.toString() + "?", "Exclude file",
                                JOptionPane.YES_NO_OPTION,
                                JOptionPane.QUESTION_MESSAGE);

                if (answer != JOptionPane.YES_OPTION)
                    return;

                String arg1 = null;

                switch (node.getType())
                {
                   case ProjectTreeNode.SCRIPT:
                       arg1 = "script";
                       break;

                   case ProjectTreeNode.LIBRARY:
                       arg1 = "library";
                       break;
                }

                try
                {
                    // for files and objects,
                    // send a request that the file is closed
                    if (node.getType() != ProjectTreeNode.LIBRARY)
                    {
                        Object[] args2 = { node.getObject() };
                        eventManager.raiseEvent(GrideEvents.CLOSE_FILE, args2);
                    }

                    Object[] args = { arg1, node.getObject() };
                    eventManager.raiseEvent(GrideEvents.PROJECT_EXCLUDE_FILE,
                                            args);
                }
                catch (UserCancelException ex)
                {
                    return;
                }
            }
        };

        excludeContextMenuItem = new JMenuItem("Exclude from project...");
        excludeContextMenuItem.setEnabled(true);
        excludeContextMenuItem.addActionListener(excludeListener);

        setAsMainContextMenuItem = new JMenuItem("Set as main script");
        setAsMainContextMenuItem.setEnabled(true);
        setAsMainContextMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                ProjectTreeNode node = (ProjectTreeNode)
                        projectTree.getSelectionPath().getLastPathComponent();
                
                GrideScript script = (GrideScript) node.getObject();                
                projectManager.setCurrentMainScript(script);

                treeModel.reloadScripts();
            }
        });

        renameContextMenuItem = new JMenuItem("Rename/Move");
        renameContextMenuItem.setEnabled(true);
        renameContextMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                ProjectTreeNode node = (ProjectTreeNode)
                        projectTree.getSelectionPath().getLastPathComponent();

                GrideScript script = (GrideScript) node.getObject();
                final Project project = projectManager.getCurrentProject();

                RenameScriptForm form = new RenameScriptForm(script,
                    project.getCompletePackageList(),
                    new RenameMoveScriptHandler()
                {
                    public boolean rename(GrideScript script, String newName,
                                       String newPackage)
                    {
                        String fullName;
                        if (!newPackage.equals(""))
                            fullName = newPackage + "." + newName;
                        else
                            fullName = newName;
                        
                        // if nothing changed, do nothing
                        if (script.getFullName().equals(fullName))
                            return false;

                        // check if another script with the same name exists
                        for (GrideScript s : project.getScripts())
                            if (s != script && s.getFullName().equals(fullName))
                            {
                                JOptionPane.showMessageDialog(null,
                                 "Can't rename/move: another script exists " +
                                 "with this name!", "Rename/Move Error",
                                 JOptionPane.ERROR_MESSAGE);
                                
                                return false;
                            }

                        // change the script name
                        script.setFullName(fullName);

                        // move the script to the new location
                        File newSource = new File(project.getSourceDir() + "/" +
                                                  fullName.replace(".", "/") +
                                                  ".groovy");
                        Utils.copyFile(script.getSourcefile(), newSource);
                        script.getSourcefile().delete();
                        try
                        {
                            script.setSourceFile(newSource);
                        }
                        catch (IOException ex)
                        {
                            return false;
                        }
                        script.setClassFile(null);
                        
                        treeModel.reloadScripts();
                        project.trySaveProject();

                        Object[] args = { script };
                        eventManager.raiseEvent(
                                        GrideEvents.PROJECT_FILE_RENAMED, args);

                        return true;
                    }
                });

                form.pack();
                form.setVisible(true);
            }
        });

        
    }

    /**
     *
     */
    @Override
    public void activate()
    {
        if (this.active)
            return;

        // if there's an open project, show the project tree
        if (projectManager.getCurrentProject() != null)
            showProjectTree();
        else
            showProjectsList(); // otherwise show a list of projects

        final Object myself = this;

        // register listener for project loaded event
        // which will open the browser when project loaded
        eventManager.addListener(GrideEvents.PROJECT_LOADED, new EventListener()
        {
            public Object whoIsListening()
            {
                return myself;
            }

            public void eventRaised(String eventID, Object[] args)
            {
                if (projectManager.getCurrentProject() != null)
                    showProjectTree();
            }
        });

        // register listener for project closed event - hide the browser
        eventManager.addListener(GrideEvents.PROJECT_CLOSED, new EventListener()
        {
            public Object whoIsListening()
            {
                return myself;
            }

            public void eventRaised(String eventID, Object[] args)
            {
                guiManager.removeComponent(projectTree);
            }
        });

        eventManager.addListener(GrideEvents.PROJECT_NEW_SCRIPT,
                                 new EventListener()
        {
            public Object whoIsListening()
            {
                return myself;
            }

            public void eventRaised(String eventID, Object[] args)
            {
                treeModel.reloadScripts();
            }
        });

        EventListener addExcludeListener = new EventListener()
        {
            public Object whoIsListening()
            {
                return myself;
            }

            public void eventRaised(String eventID, Object[] args)
            {
                String type = (String) args[0];

                if (type.equals("script"))
                    treeModel.reloadScripts();
                else if (type.equals("library"))
                    treeModel.reloadLibraries();
            }
        };

        // register listener for project file added event
        // it will refresh affected subtree
        eventManager.addListener(GrideEvents.PROJECT_FILE_IMPORTED,
                                 addExcludeListener);

        // register listener for project file excluded event
        // it will refresh affected subtee
        eventManager.addListener(GrideEvents.PROJECT_FILE_EXCLUDED,
                                 addExcludeListener);

        eventManager.addListener(GrideEvents.PROJECT_CLOSED, new EventListener()
        {
            public Object whoIsListening()
            {
                return myself;
            }

            public void eventRaised(String eventID, Object[] args)
            {
                showProjectsList();
            }
        });

        this.active = true;
    }

    /**
     *
     */
    @Override
    public void deactivate()
    {
        if (!this.active)
            return;

        // hide the browser
        guiManager.removeComponent(projectTree);
        guiManager.removeComponent(projectsList);

        // remove all listeners
        eventManager.removeAllListenersFrom(this);

        this.active = false;
    }


    private void showProjectTree()
    {
        // remove any existing browsers first and the projects list
        guiManager.removeComponent(projectTree);
        guiManager.removeComponent(projectsList);

        // set a new tree model based on the current project
        treeModel = new ProjectTreeModel(
                       new ProjectTreeNode(ProjectTreeNode.PROJECT, null,
                                           projectManager.getCurrentProject()));
        projectTree.setModel(treeModel);

        cellRenderer.setProject(projectManager.getCurrentProject());

        // add the tree to gui
        guiManager.addComponent(GUIManager.RIGHT_PANE, projectTree,
                                "Project Browser");
        guiManager.focusOnComponent(projectTree);
    }


    private void showProjectsList()
    {
        // remove the project tree first
        guiManager.removeComponent(projectTree);

        projectsList.removeAll();       
        projectsList.setListData(createProjectsList());

        // add the projects list to gui
        guiManager.addComponent(GUIManager.RIGHT_PANE, projectsList,
                                "Available Projects");
        guiManager.focusOnComponent(projectsList);
    }


    private Object[] createProjectsList()
    {
        List<String> list = new ArrayList<String>();

        list.add("Create a new project...");

        for (String projectName : projectManager.getExistingProjectNames())
            list.add(projectName);

        return list.toArray();
    }

}


class ProjectTreeModel extends DefaultTreeModel
{
    public ProjectTreeModel(TreeNode root)
    {
        super(root);
    }

    public void reloadScripts()
    {
        this.reload(((TreeNode)getRoot()).getChildAt(0));
    }

    public void reloadLibraries()
    {
        this.reload(((TreeNode)getRoot()).getChildAt(1));
    }
}
